home *** CD-ROM | disk | FTP | other *** search
- //*****************************************************************************
- // C_Report.prg
- // Report class for OBJECT v2.03
- // Copyright (c) 1991, JHK, JHK-Software, Piestany
- // Please compile with: /N/M/W/A
- //-----------------------------------------------------------------------------
-
- #include "Error.ch"
- #include "InKey.ch"
- #include "Object.ch"
-
- #define LenSp 2 //spaces beetwen report fields
-
- create class Report from DBrowse
- export:
- var FName // "" //file name for this report,
- var Handle // -1 //and its handle; default: output file not created (opened)
- var Width // 0 //report (paper) width
- var TopText // "" //"top_line1;line2;.."
- var Fields // {} //{{cTitle,cField,cPicture,lTotal,cSubTotal},...}
- var FSizes // {} //paralel array sizes of fields for report, see VProcess()
- var Totals // {} //paralel array totals for each field:{{nTotal,nSubTotal},...}
- var BottomText // "" //"bottom_line1;line2;..."
- var OnlyTotals // false //to report only totals and subtotals
- var OldOrder // 0 //last controlling index order
- method New=ReportNew //o:New()
- method Init=ReportInit //o:Init(Name,R,C,Rs,Cs,Clr,Shadow)
- method AddData=ReportAddData //o:AddData(cTop,aFields,cBottom,lOnlyTotals)
- method AddTop=ReportAddTop //o:AddTop(cTop)
- method AddField=ReportAddField //o:AddField(cTitle,cField,cPicture,lTotal,cSubTotal)
- method AddBottom=ReportAddBottom //o:AddBottom(cBottom)
- method VPaint=ReportVPaint //o:VPaint()
- method VProcess=ReportVProcess //o:VProcess()
- endclass
-
-
- //*****************************************************************************
- // Report:New() --> self
- // initialize new object
- //
- constructor ReportNew()
- ::FName:= ""
- ::Handle:= -1
- ::Width:= 0
- ::TopText:= ""
- ::Fields:= {}
- ::FSizes:= {}
- ::Totals:= {}
- ::BottomText:= ""
- ::OnlyTotals:=false
- ::OldOrder:= 0
- ::InfoBlock:= {|o|nil}
- ::DoneBlock:= {|o|DoDone(o)}
- return(self)
-
-
- //-----------------------------------------------------------------------------
- // Report::DoInfo() --> true
- // show CurRec,Index,Filter information
- //
- static function DoInfo(Report)
- Report:InfoMsg:=" "+ResTxt(052)+"="+NTrim(RecNo())+"/"+NTrim(LastRec())+;
- " "+ResTxt(053)+"="+NTrim(IndexOrd())+;
- " "+ResTxt(054)+"="+NTrim(Report:FilterNo)+" "
- Report:DoInfo()
- return(true)
-
-
- //-----------------------------------------------------------------------------
- // Report::DoDone() --> true/false
- // conditional terminate this report
- //
- static function DoDone(Report)
- if Report:Handle==-1; return(true); endif
- if Alert(ResTxt(088),ResTxt(123))<>1; return(false); endif //continue
- begin break
- FClose(Report:Handle)
- FErase(Report:FName)
- FErase(cTempFile+".ntx")
- end break
- return(true)
-
-
- //*****************************************************************************
- // Report:Init(Name,R,C,Rs,Cs,Clr,Shadow) --> true
- // initialize the report window
- //
- method function ReportInit(Name,R,C,Rs,Cs,Clr,Shadow)
- default Rs to 1
- default Cs to Min(MaxCol()-4,Max(Len(ResTxt(081))+6,Len(if(ValType(Name)=="C",Name,Eval(Name)))+10))
- default R to Int((MaxRow()-Rs)/2)
- default C to Int((MaxCol()-Cs)/2)
- if( Cs<36, Cs:=36, )
- ::MaxRows:=Rs
- ::MaxCols:=Cs
- return(::super(DBrowse):Init(Name,R,C,Rs,Cs,Clr,Shadow))
-
-
- //*****************************************************************************
- // Report:AddData(cTop,aFields,cBottom,lOnlyTotals) --> true
- // save complete report info
- //
- method function ReportAddData(cTop,aFields,cBottom,lOnlyTotals)
- default cTop:=""
- default cBottom:=""
- default lOnlyTotals:=false
- ::TopText:=cTop
- ::Fields:=AClone(aFields)
- ::BottomText:=cBottom
- ::OnlyTotals:=lOnlyTotals
- return(true)
-
-
- //*****************************************************************************
- // Report:AddTop(cTop) --> true
- // save top lines
- //
- method function ReportAddTop(cTop)
- ::TopText:=cTop
- return(true)
-
-
- //*****************************************************************************
- // Report:AddField(cTitle,cField,cPicture,lTotal,cSubTotal) --> true
- // save top lines
- //
- method function ReportAddField(cTitle,cField,cPicture,lTotal,cSubTotal)
- default cTitle to cField
- AAdd(::Fields,{cTitle,cField,cPicture,lTotal,cSubTotal})
- return(true)
-
-
- //*****************************************************************************
- // Report:AddBottom(cBottom) --> true
- // save bottom lines
- //
- method function ReportAddBottom(cBottom)
- ::BottomText:=cBottom
- return(true)
-
-
- //*****************************************************************************
- // Report:VPaint() --> true
- // paint please wait... message
- //
- method function ReportVPaint()
- @ ::Row+1,::Col+4 say ResTxt(081) color ::Color
- Eval(::InfoBlock,self)
- return(true)
-
-
- //*****************************************************************************
- // Report:VProcess() --> Report/FInfo object
- // main report method, output data into disk file
- //
- method function ReportVProcess()
- local Top,Bottom,FInfo
- local Values:={} //current field values for output
- local aSubTotal,ee,i //work info array of needed subtotals, ee,i=working for block in block problem
- local Oe //clipper error object
- local OutTask:=self //may be changed onto FInfo
- SaveDOut(ResTxt(145))
- SaveHelpIdx({1})
- ::UpDatabase() //set up good database
- begin break //keep disk errors
- if ::Handle==-1
- if CreateFile(self)==-1 //disk error
- Alert(ResTxt(090))
- break
- endif
- if !AddIndex(self)
- Oe:=ErrorNew()
- Oe:Severity:=ES_ERROR
- Oe:SubSystem:="Object/Report"
- Oe:Description:="Can't create index file"
- Oe:FileName:=cTempFile
- break Oe
- endif
- go top //make sure for top of database
- Top:=ListAsArray(::TopText,";")
- Bottom:=ListAsArray(::BottomText,";")
- ::Width:=0
- ::FSizes:=Array(Len(::Fields))
- AEval(::Fields,{|e,i|::Width+=(::FSizes[i]:=Max(Len(e[1]),Len(Transform(&(e[2]),e[3]))))+LenSp})
- ::Width-=LenSp
- ::Width:=Max(::Width,AWidth(Top))
- ::Width:=Max(::Width,AWidth(Bottom))
- *
- FWrite(::Handle,Replicate(chr(240),::Width-Len(ResTxt(086))-5)+" "+ResTxt(086)+" "+Replicate(chr(240),3)+cr_lf)
- if !Empty(Top)
- AEval(Top,{|e|FWrite(::Handle,PadC(e,::Width)+cr_lf)}) //out header
- FWrite(::Handle,Replicate("=",::Width)+cr_lf) //underline
- endif
- AEval(::Fields,{|e,i|FWrite(::Handle,PadR(e[1],::FSizes[i])+Space(LenSp))}) //title of fields
- FWrite(::Handle,cr_lf) //new line for end of titles
- AEval(::FSizes,{|e|FWrite(::Handle,Replicate("=",e)+Space(LenSp))}) //titles underline
- FWrite(::Handle,cr_lf) //new line for end of underline
- *
- if AScan(::Fields,{|e|e[4]})>0 //is any total?
- ::Totals:=Array(Len(::Fields),2)
- AEval(::Fields,{|e,i|::Totals[i,1]:=if(!Empty(e[4]),0,nil),::Totals[i,2]:=if(!Empty(e[5]),0,nil)})
- endif
- ::InfoBlock:={|o|DoInfo(o)} //#show CurRec,Index,Filter
- endif
- *--------------------------------------------------------------------------
- repeat //DbEval loop
- DoInfo(self)
- Values:={} //clear
- AEval(::Fields,{|e|AAdd(Values,&(e[2]))}) //load current values
- skip //future values
- AEval(::Fields,{|e,i|OutField(self,i,Values[i])}) //output field
- if( !::OnlyTotals, FWrite(::Handle,cr_lf), ) //new line (field or subtotal)
- aSubTotal:={}
- AEval(::Fields,{|e,i|ee:=e,AAdd(aSubTotal,TestSubTotal(self,Values,i,AScan(::Fields,{|x|x[2]==ee[5]})) )})
- if AScan(aSubTotal,{|e|e[1]>0})>0
- if !::OnlyTotals
- AEval(aSubTotal,{|e,i|OutSubTotal(self,i,if(e[1]==0," ","-"),3)})
- FWrite(::Handle,cr_lf)
- endif
- for i:=1 to Len(aSubTotal)
- if aSubtotal[i,1]>0
- OutSubTotal(self,i,i,1)
- else
- if ::OnlyTotals and AScan(aSubTotal,{|w|i==w[2]})>0
- OutSubTotal(self,i,Values[i],2)
- else
- OutSubTotal(self,i," ",3)
- endif
- endif
- endfor
- FWrite(::Handle,cr_lf)
- if( !::OnlyTotals, FWrite(::Handle,cr_lf), )
- endif
- until Eof() or PauseKey()==nSwapTask
- *--------------------------------------------------------------------------
- if Eof()
- if !Empty(::Totals)
- AEval(::FSizes,{|e|FWrite(::Handle,Replicate("=",e)+Space(LenSp))})
- FWrite(::Handle,cr_lf)
- AEval(::Totals,{|e,i|FWrite(::Handle,PadL(if(e[1]==nil," ",NTrim(e[1])),::FSizes[i])+Space(LenSp))})
- FWrite(::Handle,cr_lf)
- endif
- if !Empty(Bottom)
- FWrite(::Handle,Replicate("=",::Width)+cr_lf) //underline
- AEval(Bottom,{|e|FWrite(::Handle,PadC(e,::Width)+cr_lf)}) //out footnote
- endif
- FWrite(::Handle,Replicate(chr(240),::Width-Len(ResTxt(087))-5)+" "+ResTxt(087)+" "+Replicate(Chr(240),3)+cr_lf)
- FClose(::Handle)
- ::Handle:=-1
- DelIndex(self)
- ::Done() //dead parent task
- *
- object FInfo of FInfo
- if FInfo:Init(::FName,::Name)
- FInfo:DoneBlock:={|o|DoneViewReport(o)}
- FInfo:Wrap:=false
- FInfo:CanErase:=true
- FInfo:Paint()
- SetLastKey(K_ENTER)
- OutTask:=FInfo //child task continued without parent task
- else
- Alert(ResTxt(094))
- endif
- *
- endif
- recover break using Oe
- if Oe<>nil
- if Empty(Oe:FileName); Eval(ErrorBlock(),Oe); endif //no disk error!
- Alert(ResTxt(089)+";"+ErrorMessage(Oe))
- begin break
- FClose(::Handle)
- FErase(::FName)
- end break
- endif
- ::Handle:=-1
- ::Done()
- SetLastKey(nSwapTask) //need for task class
- end break
- ::RecNo:=RecNo()
- RestHelpIdx()
- RestDOut()
- return(OutTask)
-
-
- //-----------------------------------------------------------------------------
- // Report::AddIndex() --> true/false
- // create new need index for subtotals, save old index info
- // see UpDatabase()
- //
- static function AddIndex(Report)
- local OneDbf,i
- local c:=GetNewIndex(Report) //new index expression (as string)
- Report:OldOrder:=IndexOrd() //save last order
- if !Empty(c)
- i:=1
- while !Empty(IndexKey(i)) and !(IndexKey(i)==c); i++; endwhile //is the index in list of active indexes
- if !(IndexKey(i)==c)
- if !Empty(IndexKey())
- c+="+"+Stringify(IndexKey())
- endif
- OneDbf:=CopyOneDbf(Alias()) //get current (alias) database definition
- i:=Len(OneDbf:Ntx)+1 //new index order
- OneDbf:AddNtx(,cTempFile,c)
- if !OneDbf:NtxOpen(); return(false); endif
- endif
- DbSetOrder(i)
- endif
- return(true)
-
-
- //-----------------------------------------------------------------------------
- // Report::GetNewIndex() --> cNewIndexKey
- // create new need index key (as string)
- //
- static function GetNewIndex(Report)
- local c:=""
- AEval(Report:Fields,{|e|if(e[5]<>nil,c+="+"+Stringify(e[5]),nil)})
- return(SubStr(c,2))
-
-
- static function Stringify(Field) //cFieldName
- local cC:=ValType(&(Field))
- do case
- case cC=="M"; return(Field)
- case cC=="C"; return(Field)
- case cC=="D"; return("DTOS("+Field+")")
- case cC=="N"; return("STR("+Field+")")
- case cC=="L"; return("IF("+Field+",'.T.','.F.')")
- endcase
- return(true)
-
-
- //-----------------------------------------------------------------------------
- // Report::DelIndex() --> true
- // restore original index system
- //
- static function DelIndex(Report)
- local OneDbf:=GetOneDbf(Alias()) //get current (alias) database definition
- OneDbf:NtxOpen(false)
- DbSetOrder(Report:OldOrder)
- NetFErase(cTempFile+".ntx",true)
- return(true)
-
-
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // Report::OutField(i,xValue) --> true
- // output one field of line of report
- //
- static function OutField(Report,i,xValue)
- local c
- if !Report:OnlyTotals
- c:=Transform(xValue,Report:Fields[i,3]) //picture transformation
- c:=if(ValType(xValue)=="N", PadL(c,Report:FSizes[i]), PadR(c,Report:FSizes[i]))
- FWrite(Report:Handle,c+Space(LenSp)) //out value
- endif
- if !Empty(Report:Totals)
- if Report:Totals[i,1]<>nil; Report:Totals[i,1]+=xValue; endif //total
- if Report:Totals[i,2]<>nil; Report:Totals[i,2]+=xValue; endif //subtotal
- endif
- return(true)
-
-
- //-----------------------------------------------------------------------------
- // Report::TestSubTotal(Values,i,j) --> i/0
- // output one field of line of report
- // i=field index into Report:Fields, this field has been sumarized
- // j=0 do not subtotal
- // j>0 and Values[j]<>FutureValue(Report:Fields[j,2]) do subtotal
- //
- static function TestSubTotal(Report,Values,i,j)
- if j==0; return({0,0}); endif
- if Values[j]==&(Report:Fields[j,2]); return({0,0}); endif
- return({i,j})
-
-
- //-----------------------------------------------------------------------------
- // Report::OutSubTotal(Report,i,xValue,nMode) --> true
- // output one field of line of report
- // i=field index into Report:Fields, this field has been sumarized
- // xValue=" " in this time will be output spaces or
- // xValue="-" in this time will be only underlining
- // xValue=i write Report:Totals[i,2]
- // nMode=1 output totals_number
- // nMode=2 output totals_field
- // nMode=3 output spaces or "-----"
- //
- static function OutSubTotal(Report,i,xValue,nMode)
- local c
- if nMode==1
- c:=Transform(Report:Totals[i,2],Report:Fields[i,3]) //picture transformation
- FWrite(Report:Handle,PadL(AllTrim(c),Report:FSizes[i])+Space(LenSp)) //out value
- Report:Totals[i,2]:=0 //clear subtotal
- elseif nMode==2
- c:=Transform(xValue,Report:Fields[i,3]) //picture transformation
- FWrite(Report:Handle,PadL(AllTrim(c),Report:FSizes[i])+Space(LenSp)) //out value
- else
- FWrite(Report:Handle,Replicate(xValue,Report:FSizes[i])+Space(LenSp))
- endif
- return(true)
-
-
- //-----------------------------------------------------------------------------
- // Report::CreateFile() --> Handle
- // look for existing files and create new (unique) report file
- //
- static function CreateFile(Report)
- Report:FName:=GetNewRepName()
- Report:Handle:=FCreate(Report:FName)
- return(Report:Handle)
-
-
- //-----------------------------------------------------------------------------
- // FInfo::DoneViewReport() --> true/false
- // selectable erasing report file
- //
- static function DoneViewReport(FInfo)
- local Ch
- FInfo:Top(false)
- Ch:=Alert(ResTxt(091)+" "+FInfo:FName+";"+ResTxt(092),ResTxt(132))
- do case
- case Ch==1
- FErase(FInfo:FName)
- return(true)
- case Ch==2
- return(true)
- endcase
- return(false) //dummy line
-
- //------------------------------------------------------- eof (c)JHK ----------
-
-